home *** CD-ROM | disk | FTP | other *** search
Text File | 1991-02-15 | 66.3 KB | 1,352 lines |
- Set your editor's TAB width to 3
-
- THE IFF ILBM LIBRARY MANUAL
- ¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡¡
- by Jeff Glatt
- 6 Sycamore Drive East
- New Hartford, NY 13413
-
- *****************************************************************************
-
- 1). Introduction
- 2). Overview
- 3). High Level Routines
- 4). Middle Level
- 5). Handling PROPS
- 6). Custom FORM Handler
- 7). Custom (ILBM) Chunk Handler
- 8). Low Level
- 9). Adapting old applications
- 10). ANIM Support
- 11). Image Size/Scaling
- 12). ILBMLib Error Msgs
- 13). Misc Routines
- 14). Additional Comments
- 15). Acknowledgements
- 16). Scary Legal Stuff
-
-
- *****************************************************************************
- 1). Introduction
-
- The primary purpose of the ilbm.library is to provide functions to read and
- write any kind of IFF file. In particular, there is extra, high level support
- for ILBM picture files which simplifies the viewing or saving of a picture.
- Furthermore, there are routines useful for creating an ANIM reader/writer/
- player.
- The ilbm.library is a disk-based runtime library. This means that any
- application, upon being run, can open and call routines in this library just
- like a program might utilize routines in the Intuition or Graphics libraries.
- The ilbm library is easily accessed from C, assembly, and BASIC programs, and
- there are examples available in each language.
- The library is based upon the original Electronic Arts IFF code (i.e. Open-
- RIFF, GetBODY, etc.) as well as a few Commodore enhancements (i.e. Scheppner's
- getBitMap, handleCAMG, etc.). There have been a few changes to the internal
- logic of some routines, but generally, they perform the same functions as the
- original code. A few more significant changes were made to certain routines in
- order to accomodate ANIM and non-ILBM files, and also to streamline the parsing
- of LISTs, CATs, and PROPs.
- The biggest change between the original code and this library is that the
- code has been rewritten in the tightest possible 68000 assembly. The size of
- the library is < 7000 bytes. A program that uses this library can be smaller
- and faster than if the program used the original EA code.╣ Because the library
- uses no global data and is therefore re-entrant, many applications can use the
- library simultaneously.
- Finally, access to the low level IFF functions is provided. The routines,
- OpenRIFF, GetChunkHdr, IFFReadBytes, IFFWriteBytes, etc. are all included.
- These routines are equivilent to the original EA code except that the lib code
- is smaller and faster. Because of this, it is possible to use this library for
- any kind of IFF reader and writer with a resulting improvement in the applica-
- tion's size and speed.
- The current version is 0.5.
-
-
- *****************************************************************************
- 2) Overview
-
- The library has functions that fall into three levels: high, middle, and
- low. The high level is designed to read or write ILBM files with tremendous-
- ly little effort on the part of the application. The middle level requires
- more setup, but allows you to construct a reader/writer for any kind of IFF
- file. This level handles extremely ugly details like LISTs, CATs, error
- checking for IFF "weirdness", and DOS I/O while allowing you control over what
- is to be done with individual chunks. The low level maintains DOS I/O and
- error checking, but leaves you to deal with LISTs and CATs. Anything that you
- used to do with the original EA code, you should be able to do in an almost
- identical manner, and adapting your present IFF code to use the library is
- quite easy.
- Most library routines return an IFFP code. This is a number from 0 to -11,
- or in some cases, a 4 byte IFF ID. 0 (IFF_OKAY) usually means success. The
- negative numbers mean errors. See the INCLUDE files for all possible numbers.
-
-
- *****************************************************************************
- 3). High Level Routines
-
- At the highest levels, the type of IFF file that this library is designed
- to read and write is an ILBM.
- There are 2 high level functions, LoadIFFToWindow() and SaveWindowToIFF().
- The former loads an ILBM into a window, the latter saves a window as an ILBM.
- These functions are so comprehensive that they can even be used with inter-
- preted languages like BASIC or Rexx.
- For saving a file, SaveWindowToIFF() is passed the filename that you
- wish to create, and the pointer to the window (whose screen's bitplanes are)
- to be saved in IFF ILBM form. The library writes out a complete ILBM with
- the proper BMHD, CMAP, CAMG, and BODY chunks. All error checking is done by
- the lib. SaveWindowToIFF() returns an IFFP number. 0 means that the save
- was successful. Any other number is an error. In the case of error, any
- partial file is deleted.
- When reading a file, LoadIFFToWindow() takes the filename that you wish
- to load, and a special ILBMFrame structure. See the INCLUDE files for a
- description of this structure. The C include file is "ILBM_lib.h". The assembly
- include is "IFF.i". Basic users should read "BasicUsers".
- You MUST allocate this structure and initialize certain fields before
- using LoadIFFToWindow().
- LoadIFFToWindow() looks for ILBMs inside of CATs and LISTs, sidestepping all
- of the other non-ILBM FORMs. It rummages around inside of such a file looking
- for the first ILBM that it can find. If it finds an ILBM, it does one of two
- things:
-
- 1). Loads the imagery into a window that you opened, scaling the picture
- to fit the dimensions of the window if that is necessary. What this
- means is that you can load a HIRES image into an opened LORES window,
- and vice versa, etc.
-
- 2). Opens a screen and borderless backdrop window that best "fits" the
- picture (e.g. the proper width, height, depth, and viewmodes.
-
- You determine which method is imployed by how you set up the ILBMFrame
- before calling LoadIFFToWindow(). The ILBMFrame has iWindow and iScreen
- fields. If you zero both these fields, then the library will open a screen
- and window into which it loads the picture. The pointers for this new screen
- and window will be stored in their respective ILBMFrame fields. The window
- will have a default IDCMP of MOUSEBUTTONS. If instead, you open your own
- window and screen, you should place these addresses into the ILBMFrame fields
- before calling LoadIFFToWindow(). The lib will then load the image into that
- window.
- Furthermore, you should set up the iUserFlags field. When certain bits of
- this field are set, the library will do such things as make the mouse pointer
- invisible, or not change your window's present colormap, etc. See the INCLUDE
- files for details. Usually, you'll just zero iUserFlags.
- You do not have to initialize any of the other ILBMFrame fields.
- LoadIFFToWindow() returns an IFFP number. 0 means that the load was
- successful. Any other number is an error.
- Note that if the library opens the window/screen for you, the pointers are
- returned in the ILBMFrame. From this point on, these are your responsibility.
- You may ModifyIDCMP() the window, or anything else you desire. You must close
- the window and screen when you are finished with them. Even if LoadIFFToWin-
- dow() returns an error, you must still get the iWindow and iScreen fields, and
- close any non-zero pointer. See the example programs for details.
- If neither high level routine serves your purpose, then you will need to
- use some mid or low level routines instead.
-
- ============================ SaveWindowToIFF ===========================
- IFFP = SaveWindowToIFF(fileName, window)
- d0 d1 a0
-
- Saves a window's image as an IFF ILBM file. Returns an IFFP code (0 if OK,
- a negative, non-zero number for an error. Z-flag set accordingly.)
- This procedure calls SaveILBM(), passing in an <x, y> location of <0, 0>,
- and a NULL mask. It also assumes you want to write out all the bitplanes
- in the BitMap. Also, it applies byte run compression to the BODY.
- If an error in saving, it deletes the partial file. The fileName should be
- a complete path as might be typed at the CLI (i.e. df0:extras/myName). This
- is an ascii, NULL-terminated string. window is the address of the opened
- window (as returned from Intuition's OpenWindow).
-
- ============================= LoadIFFToWindow ===========================
- IFFP = LoadIFFToWindow( fileName, ILBMFrame )
- d0 d1 a1
-
- Loads an ILBM file into the ILBMFrame's iWindow. If iWindow is NULL, opens
- a screen/backdrop window for the image. This routine calls LoadILBM().
-
-
- ******************************************************************************
- 4). Middle Level
-
- The middle level routines isolate you from dealing with low level struc-
- tures, LISTs, and CATs, but give you the option of installing custom routines
- to handle FORMs, PROPs, or chunks inside of ILBM FORMs.
- There are two mid level routines for reading/writing ILBMs, LoadILBM()
- and SaveILBM(). A third routine, LoadIFF(), is used for non-ILBM forms
- such as SMUS. There is a routine that makes saving ANIMs easy, SaveANIM().
- Furthermore, LoadILBM() can easily be setup to parse an ANIM file.
- We will discuss LoadILBM() and SaveILBM() first.
-
- ========================== SaveILBM() ==============================
- IFFP=SaveILBM(ViewModes,Compress,fileHandle,Mask,colors,BitMap,xyPoint,handler)
- d0 d0 d1 d2 d3 a0 a1 a2 a3
-
- ViewModes - The viewmodes of the screen as an ULONG
- Compress - Compression type. 0=None 1=cmpByteRun1
- fileHandle - DOS handle of the opened file
- Mask - pointer to a mask plane, or NULL if none
- colors - pointer to the colorTable to save as a CMAP chunk
- BitMap - pointer to the Bitmap structure whose planes are to be saved
- as the ILBM BODY
- xyPoint - pointer to a structure holding two UWORD values that describe
- the xy position to be saved in the BMHD chunk
- handler - pointer to a routine to be called before the BODY is written
- out, or NULL if none
-
- SaveILBM() writes an entire BitMap as a FORM ILBM in an IFF file. Unlike
- SaveWindowToIFF, this routine allows you to save an uncompressed image, or with
- a different xy position than 0,0 (for saving a portion of the whole image),
- or saving additional chunks in addition to BMHD, CAMG, CMAP, and BODY. It
- works for any display mode.
- You must open the IFF file to be written before calling SaveILBM(), and
- close the file upon return.
- SaveILBM() writes out a BMHD whose values are determined by the passed
- BitMap's size/depth and the xyPoint. A CAMG chunk is written for the passed
- ViewModes. A CMAP chunk is written for the passed colTable.
- Then, if you have supplied a passed handler, it is called. This allows an
- application to save additional chunks such as CRNG, etc. The lib passes your
- handler a GroupContext structure. To write a chunk, you must open the Context
- (via OpenRGroup()) and use PutCk(), PutCkKnown(), or calls to IFFWriteBytes
- (after PutCkHdr, and ending with PutCkEnd). Your handler should return
- IFF_OKAY after it has successfully written out the desired chunks. Any other
- IFFP code will terminate SaveILBM() with that error.
- Finally, SaveILBM() writes the BODY based on your passed compress mode
- and your mask plane.
- SaveILBM returns IFF_OKAY if successful. If an error, you must delete the
- partial file yourself.
- The utility program IFFCheck would print the following outline of the
- resulting file:
-
- FORM ILBM
- BMHD
- CAMG
- CMAP
- ...other chunks that your handler saves
- BODY (compressed) or (uncompressed)
-
-
- =========================== LoadILBM() ===========================
- IFFP = LoadILBM(fileHandle, vectors, iframe)
- d0 d1 a0 a1
-
- fileHandle - DOS handle of an opened file
- vectors - a pointer to a Vectors structure
- iframe - a pointer to the master, initialized ILBMFrame structure
-
- Can read an ILBM file. Unlike LoadIFFToWindow, you can arrange for your own
- custom routines to handle FORMs, PROPs, and certain ILBM chunks. LoadILBM()
- will return IFF_DONE if an image has been successfully loaded into a window by
- the lib's default 'FORM' handler. All other IFFP codes indicate that an image
- wasn't loaded.
- In the case of custom handlers, normal return is IFF_OKAY when the whole
- file has been scanned. On the other hand, your custom handler can cause
- LoadILBM() to terminate by returning any IFFP error (i.e. IFF_DONE, END_MARK,
- IFF_BAD, etc). In this case, LoadILBM() returns that IFFP error.
- ILBMFrame's iWindow, iScreen, and iUserFlags must be initialized.
-
- EXTREMELY modified version of ReadPict.c
- by Jerry Morrison, Steve Shaw, and Steve Hayes, Electronic Arts.
- Modified by C. Scheppner and Jeff Glatt
-
-
- The library has default routines that it calls whenever it encounters a
- LIST, CAT, PROP, FORM, or a chunk inside of a PROP or FORM. LISTs, CATs, and
- ILBM PROPs are always handled by the library. On the other hand, you can
- replace the default handlers for non-ILBM PROPs, all FORMs, or all ILBM chunks
- with your own custom routines. For example, the library can call your FORM
- handler each time that it encounters a FORM inside of an IFF file.
- Let me explain some details about the default routines. First, the default
- FORM handler looks for ILBMs. It can be setup to parse ANIMs and other non-
- ILBM forms, but it always looks for ILBMs. For this reason, you should always
- supply an ILBMFrame when using the default FORM handler. The handler can find
- ILBMs inside of CATs and LISTs, sidestepping all of the other non-ILBM FORMs.
- It rummages around inside of such a file looking for the first ILBM that it can
- find. It handles the following chunks inside an ILBM:
-
- CMAP BMHD CRNG CCRT CAMG BODY
-
- The CMAP, BMHD, CAMG, CCRT, and CRNG chunks are loaded into the respective
- fields of the ILBMFrame (see INCLUDE file). CCRT are converted to CRNG. All
- other ILBM chunks are ignored. When it finds the BODY of an ILBM, it does one
- of two things:
-
- 1). Loads the imagery into a window that you opened, scaling the picture
- to fit the dimensions of the window if that is necessary.
-
- 2). Opens a screen and borderless backdrop window that best "fits" the
- picture (e.g. the proper width, height, depth, and viewmodes).
-
- This should sound familiar. It is exactly the same as LoadIFFToWindow. Just
- like that high level function, you should set up the ILBMFrame according to
- what you want the lib to do. After the image is loaded, the parsing is stopped
- and LoadIFF() returns IFF_DONE if successful. All other error codes indicate
- failure.
- The default PROP handler handles the following chunks inside an ILBM PROP:
-
- CMAP BMHD CRNG CCRT CAMG
-
- These chunks are loaded into the respective fields of the ILBMFrame with CCRT
- converted to CRNG. All other ILBM chunks are ignored. Also, any non-ILBM
- PROPs are ignored.
- One of the parameters to LoadILBM() is a Vector structure. This is simply
- a structure that holds the pointers to whatever routines you would like the
- lib to execute while parsing an IFF file. These routines replace the lib's
- default handlers. If the Vector pointer is 0, then the default lib routine is
- used. You must initialize this structure before calling LoadILBM()!
- One field holds a pointer to a routine to handle non-ILBM 'PROP's or an
- ILBM PROP chunk that is "unknown" to the lib. The lib doesn't know about the
- following ILBM chunks: ANHD, DEST, GRAB, SPRT, and DLTA. It will skip these
- in an ILBM PROP if you don't install a PROPhandler. Also, it will skip nonILBM
- PROPs if you don't have a PROPhandler. If you don't care, set this field to
- NULL.
- Another field is for your FORMhandler routine address. If not NULL, this
- routine is called instead of the lib's default 'FORM' handler. You are expected
- to handle all parsing of FORMs as the library finds them. This means that you
- also have to parse the chunks in the FORM. If your application does not deal
- with ILBMs at all, you will have to install a custom FORM routine (and use
- LoadIFF).
- Another field in the Vectors structure is for a CHUNKhandler. This is called
- by lib's default 'FORM' handler for all chunks inside an ILBM. If NULL, the
- lib's CHUNKhandler will skip the following ILBM chunks:
-
- ANHD DEST GRAB SPRT DLTA
-
- If you don't use the lib's default 'FORM' handler, then this field is ignored.
- If you supply a CHUNKhandler, you are telling the library, "If you come across
- an ID that you don't understand, don't skip it. Instead, give it to me and I'll
- take care of that ILBM chunk".
- The last field is for the NonILBMhandler. This is called by the lib's
- default 'FORM' handler when it encounters a FORM other than an ILBM (i.e. 8SVX,
- etc). If NULL, nonILBM FORMs are skipped over in search of ILBMs. If you
- don't use the lib's default 'FORM' handler, then this field is ignored. If you
- supply a NonILBMhandler, you are telling the library, "If you come across a
- FORM other than ILBM, don't skip it. Instead, give it to me and I'll take care
- of that FORM". Note that the library still looks for an ILBM to load into a
- window when you use the default 'FORM' routine.
- So, by installing routines via the Vectors structure, you can "take away"
- certain parsing duties from the lib, while it handles LISTS, CATS, and low
- level details. For example, you could install just a CHUNKhandler and set
- the other fields to 0. The lib would parse all LISTS, PROPS, CATS, and FORMS.
- It would only call your routine for each chunk in an ILBM. See the examples,
- ANIMInfo.asm and IFFinfo.c, for details of installing custom routines, and
- using mid level routines.
- If your application does not deal with ILBMs, you will need to replace the
- default FORMhandler and PROPhandler. If you are dealing with ANIMs, you will
- only need a custom CHUNKhandler to handle DLTA and ANHD chunks, plus a custom
- PROPhandler for those chunks. If you want to load other forms in addition to
- ILBMs, then you will need to add a NonILBMhandler.
- Here are the parameters passed to your custom vectors. Return an IFFP code.
-
- IFFP = PROPhandler(chunkID,PropID,Context,Vectors,Frame,PROPList)
- d0 d0 d2 a0 a2 a3 a4
-
- IFFP = FORMhandler(chunkID,Context,Vectors,Frame,PROPList)
- d0 d0 a0 a2 a3 a4
-
- NonILBMhandler and CHUNKhandler same args as FORMhandler.
-
- LoadILBM() zeros out the ILBMFrame's iFlags (but not iUserFlags), iBMAP,
- iNumColors, and iCycleCnt fields. You must initialize the iUserFlags field
- prior to calling this routine. Various bits of this field affect certain
- options (see Include File for details). Other bits are set by the lib to
- inform you of certain facts. (i.e. the ANIMB bit would be set by the default
- 'FORM' handler if an ANIM FORM was encountered in the file.) You also initial-
- ize the iWindow and iScreen fields as per LoadIFFToWindow().
-
-
-
- ====================== LoadIFF() =====================
-
- IFFP = LoadIFF(file, vector, dataAddress)
- d0 d1 a0 a1
-
- This is just like LoadILBM() except that it is for reading/writing non-ILBM
- forms. If you're using this routine, you should definitely set up the Vector's
- FORMhandler to point to a custom routine. Your custom routine should then
- parse FORMs using low level functions such as GetFChunkHdr, IFFReadBytes, etc.
- The dataAddress parameter is optional. It could be a pointer to a Frame of some
- sort (or it could be anything). The lib will pass this to your FORMhandler
- as the Frame parameter. It's up to your FORMhandler to decide what it really is
- and what to do with it.
- Normal return is IFF_OKAY if whole file scanned. On the other hand, your
- FORMhandler can cause LoadIFF to terminate by returning any IFFP error (i.e.
- IFF_DONE, END_MARK, IFF_BAD, etc). In this case, LoadIFF() returns that IFFP
- error.
-
- ====================== SaveANIM() ======================
- IFFP = SaveANIM(ViewModes,Compress,fileHandle,Mask,colors,BitMap,xyPoint,
- d0 d0 d1 d2 d3 a0 a1 a2
-
- FrameHandler,ANHDaddress)
- a3 a4
-
- Writes an ANIM file. Writes the passed BitMap as the first frame (FORM
- ILBM). Assumes user wants to write out all planes of the BitMap. Calls
- application FrameHandler() for writing subsequent frames. Normal return
- result is IFF_OKAY. Works for any display mode.
-
- The utility program IFFCheck would print the following outline of the
- resulting file:
-
- LIST
- PROP ILBM
- BMHD
- CAMG
- CMAP
- ANHD ;if passed in
- FORM ANIM
- FORM ILBM
- BODY (compressed) or (uncompressed)
- ....application routine's saved chunks (i.e. additional FORM ILBMs)
-
- This is similiar to SaveILBM except your FrameHandler is called repeatedly
- (so you can write numerous frames) while return = IFF_OKAY. Returning
- a 1 stops the Handler loop successfully. Returning an IFFP error aborts
- the save. Note that the file written is a LIST with a PROP ILBM. This is
- so that each frame need not contain identical data. If the passed ANHD
- address is not NULL, the ANHD chunk will be written in the PROP. In this
- way, a simple ANIM can be written where the first frame need only contain
- a BODY chunk, and subsequent frames contain a DLTA. If all successful,
- final return is IFF_OKAY. If an error, you must delete the partial file.
- If no FrameHandler, pass a 0.
-
-
- ******************************************************************************
- 5). Handling PROPs
-
- LoadILBM() (or LoadIFF()) creates a special PROPList to take care of any
- PROPS in the file. A PROPList is a linked list structure used to manage Frames
- allocated to hold PROP data. There are 4 routines for allocating, examining,
- and freeing frame structures from a PROPList.
- In order to link a Frame structure (i.e. an ILBMFrame) into this list, we
- need to "extend" the Frame with a few extra fields at the beginning of the
- structure. We prepend the following fields to the frame, and refer to the
- whole thing as a PropFrame.
-
- ULONG *NextPropFrame; /* Pointer to the next frame in PROPList */
- LONG ifID;
- USHORT PropFrameSize;
-
- So an ILBMPropFrame would be as follows:
-
- typedef struct {
- ULONG *NextPropFrame;
- LONG ifID;
- USHORT PropFrameSize;
- struct ILBMFrame PropFrame; /* This is the data part of the PropFrame */
- } ILBMPropFrame;
-
- So an ILBMPropFrame has three extra fields prepended to it. It is 10 bytes
- larger than an ILBMFrame. The actual, imbedded Frame (i.e. after the first
- 3 fields) I'll refer to as the data part of the PropFrame.
-
- In assembly, the prepended fields are:
-
- Next dc.l [address of next frame in list]
- ID dc.b [the 4 byte type ID] ;type of PropFrame
- Size dc.w [size] ;the size of the entire structure (with the
- ;subsequent data part)
-
- And an ILBMPropFrame would be as follows:
-
- Next dc.l 0
- ID dc.b 'ILBM'
- size dc.w SizeOfILBMFrame+10
- ;an ILBMFrame structure immediately follows. This is the data part.
-
-
- If an ILBM PROP (inside of a LIST) is encountered, the library always
- allocates an ILBMPropFrame on your behalf, linking it into the PROP list.
- Note that the library ALWAYS takes care of parsing the following chunks in
- an ILBM PROP:
-
- BMHD CMAP CAMG CRNG CCRT
-
- It parses the chunk data into the appropriate fields of the ILBMPropFrame. For
- other ILBM chunks (i.e. ANHD, DEST, etc), it will call your PROPhandler, pass-
- ing the chunkID and a pointer to the newly allocated ILBMPropFrame. The
- PropID will be ID_ILBM. You are expected to handle loading/parsing the chunk
- data, returning IFF_OKAY if successful. You should either store the chunk's
- data somewhere, or use it to modify certain fields of the passed ILBMPropFrame.
- Returning any other negative IFFP error terminates LoadILBM() (or LoadIFF)
- which also returns that error.
- For non-ILBM PROPs, your custom PROPhandler is also called. The PropID is
- the type ID of the PROP (i.e. SMUS, 8SVX, SAMP). The chunkID is meaningless.
- You should either return IFF_OKAY to ignore the PROP, or parse the PROP using
- the low level functions GetPChunkHdr and IFFReadBytes. A convenient approach
- would be to allocate a PropFrame for the IFF type. (It's up to your program to
- define other types of PropFrames. What's an SMUSPropFrame look like? I don't
- know.) Then link it into the PROPList. Use GetPROPStruct() to do all this.
- Then parse the PROP chunk data into this allocated PropFrame. Later, your
- custom FORMhandler or NonILBMhandler can retreive the desired PropFrame via
- SearchPROP(). Your PROPhandler should return an IFFP code. Anything but
- IFF_OKAY aborts the load with that error.
-
- If no custom PROPhandler, non-ILBM PROPs and ILBM chunks ANHD, DEST, GRAB,
- SPRT, and DLTA are ignored.
-
-
- ======================= GetPROPStruct ======================
- Frame = GetPROPStruct(size,typeID,PROPList)
- d0 d0 d1 a1
-
- Allocates a PropFrame of passed size, and stores its ID, links it at the
- tail of the passed PROPList, and increments PROPList's entries. Returns a
- pointer to the PropFrame's data part (imbedded Frame), or 0 if error.
- Note that a pointer to the data part is returned (i.e. skips those first
- 3 "extra" fields) so that you can treat the returned pointer just like a
- regular frame. If you were allocating an ILBMPropFrame, the returned pointer
- would be that of an ILBMFrame.
- The size must include the 3 extra fields.
-
-
- ======================== SearchPROP() =====================
- Frame = SearchPROP(ID,PROPList)
- d0 d0 a1
-
- Searches the passed PROPList for the last PropFrame with the same ID as
- passed. If it finds one of the same type, returns the address of the data part
- (the imbedded frame), or 0 if none of that type found in the list.
- Used by your custom FORMhandler to search the PROPList for any PropFrames
- with the same ID.
-
-
- ======================== CopyILBMProp() ======================
- CopyILBMProp(FromFrame,ToFrame)
- d0 a1
-
- Copies FromFrame to ToFrame. Both frames must be ILBM frames. It transfers
- the iUserFlags field of FromFrame to ToFrame without altering any set bits in
- ToFrame's iUserFlags.
- If SearchPROP() returns an Frame, you'll want to copy that Frame to the
- Frame you intend to use to parse the FORM. This is a routine for copying
- ILBMFrames. You'll need to write routines to copy other Frames that you devise.
-
-
- ======================= FreePROPList() ======================
- FreePROPList(PROPList)
- a1
-
- Frees all the PropFrames in the passed PROPList. Normally, LoadILBM() and
- LoadIFF() do this. When using LoadILBM() or LoadIFF(), the lib allocates and
- initializes the PROPList. Those routines also free up that PROPList and all its
- PropFrames upon termination of the load. This function is provided in case you
- are maintaining a second PROPList that you create and need to free.
-
-
- *****************************************************************************
- 6). Custom FORM handler
-
- You'll need a custom FORM handler only if you aren't interested in ILBMs.
- In this case, you'll also use LoadIFF().
- When the lib encounters a FORM, your custom FORM handler is passed a pointer
- to an initialized Context structure (to be discussed later). Although no
- further initialization of this structure is required on your part, you may need
- it for calling certain low level lib functions. You are also passed your master
- ILBMFrame so that you can save data to it from within your FORMhandler. You are
- passed the type ID of the FORM (i.e. is it an 'ILBM'?). Also, you are passed
- a pointer to the PROP list. One of the first things you'll probably want to do
- is search it for any PropFrame with the same type ID as the FORM. The function
- SearchPROP does this. You may then copy that Frame's data into the Frame that
- is used to hold data within your FORMhandler. CopyILBMProp() can copy an
- ILBMPropFrame to your master ILBMFrame. Then, as you parse chunks in your
- FORMhandler, the new data will overwrite the corresponding PROP data (as it
- should). You also are passed the Vectors structure address in case you need it
- further. Your FORMhandler is expected to parse the FORM (using a few low level
- routines) and eventually return an IFFP code. Anything but IFF_OKAY aborts the
- load.
- You should use GetFChunkHdr() to read the header of each chunk inside a
- FORM. You pass this the GroupContext structure (which the lib passed to your
- custom FORM handler). It returns an IFFP code. This should be the chunk ID
- if all went well. Otherwise, it will be a negative IFFP number. (Remember,
- legal IFF IDs are positive numbers).
-
- ID = GetFChunkHdr(context)
- d0 a0
-
- The exception to this is ANIM files. You should use GetF1ChunkHdr for
- ANIM files since ANIMs contain imbedded FORMs (ILBMs). Note that this will
- cause the lib to reenter your custom FORM routine for each "frame" of the
- ANIM. See IFFinfo.c for an example of a custom FORM routine handling ANIMs
- and ILBMs. The return is the same as GetF1ChunkHdr.
-
- ID = GetF1ChunkHdr(context)
- d0 a0
-
-
- ID = GetPChunkHdr(context)
- d0 a0
-
-
-
- ******************************************************************************
- 7). Custom (ILBM) CHUNK Handler
-
-
- You'll use a custom CHUNKhandler only when you also use the default FORM-
- handler. By using a custom CHUNK handler, you determine what is done with
- each chunk in an ILBM. You are passed the chunkID so that you can determine
- what you are dealing with (i.e. a CMAP, a DLTA, etc.). If you don't want the
- chunk, simply return IFF_OKAY. Otherwise, you are responsible for parsing
- the rest of the chunk. The size is gotten via the passed GroupContext and the
- ChunkMoreBytes macro (see INCLUDE files). NOTE: Do not use DOS Write() to
- read in bytes. Always use IFFReadBytes().
- If you would prefer the library to handle the chunk, return the chunkID.
- The library can handle CMAP, CRNG, CCRT, BMHD, CAMG, and BODY chunks. For the
- first five chunks, the data is parsed into the ILBMFrame. If a BMHD or a CAMG,
- the respective iFlags bit is set so that you'll know that this property was
- found. For CMAP, the iNumColors reflects how many color regs were loaded into
- iColorTable. For the BODY chunk, the lib decompresses the image into the
- ILBMFrames iWindow (or opens a window if NULL), sets the screen's colors to the
- iColorTable values, and terminates LoadILBM() with IFF_DONE.
- You might not want the lib to decompress the BODY. Perhaps you are writing
- an ANIM reader where you don't want the lib to actually load the first frame
- into some window's bitmap's planes. You may prefer to decompress BODY and DLTA
- chunks during playback. You can prevent the lib from doing this by handling
- ID_BODY yourself (in your CHUNKhandler) rather than returning this ID.
- If you handle a chunk yourself, your CHUNKhandler should return IFF_OKAY
- to continue parsing the file, or any other negative IFFP number (including
- IFF_DONE) to stop the load. It is recommended that you use IFF_DONE to
- successfully abort a load since this is what the default FORM handler
- does when it reaches an ILBM BODY. This will insure that LoadILBM() always
- returns IFF_DONE for aborting the load successfully, END_MARK if you parsed the
- entire file without aborting, and other IFFP codes for error conditions.
-
- NOTE: The ANIMFLAG bit of iUserFlags will be set if you are parsing an ANIM
- FORM. Remember that the lib calls your CHUNKhandler for ANHD and DLTA,
- so for each DLTA you encounter, that is the end of another ANIM Frame
- (imbedded ILBM FORM). If ANIMFLAG is not set, then you are in a plain
- ILBM file.
-
-
- ******************************************************************************
- 8). Low Level
-
- If the high level functions LoadIFFToWindow() and SaveWindowToIFF(), or
- the mid-level functions SaveILBM(), LoadILBM(), and LoadIFF() aren't suitable,
- you may use the low level routines to create a reader/writer.
- You will have to keep track of LISTs, CATs, and PROPs as there are no
- Vectors Structure, PropFrames, and custom handlers at this level.
- The low level routines are the same routines as the original EA code, IFFr.c
- and IFFw.c, except that they are smaller and faster asm modules.
- At this point, I need to warn you that the arguments passed to some routines
- have been changed, as well as the order of the arguments. This was done to make
- the C interface code smaller and faster in assembly by using the movem in-
- struction. Mostly, the changes are trivial. In fact, the documentation for
- these functions is almost the original text.
- Unless otherwise stated, the normal (successful) return codes for the low
- level routines is IFF_OKAY. The Z and N Flags are set appropriately for low
- level functions. This means that assembly programmers can always bne to an
- error routine.
- These routines ASSUME that they're the only ones reading/writing to the
- file. You should only use these routines to read, write, or skip chunks in
- an IFF file. Do not use DOS Read, Write, or Seek.
- The library's low level routines utilize a 36 byte structure called a
- "GroupContext". The GroupContext structure that the library uses is identical
- to the original EA structure. Check the INCLUDE files for a description.
- The Frame field is now called UserData, but you can use it to pass a pointer
- to a Frame from level to sublevel.
- For the high and mid-level routines, the lib allocates a new Context (and
- initializes it by OpenRIFF or OpenRGroup) for every group (FORM, CAT, LIST,
- or PROP) encountered. This is done for you even if you have custom vectors
- for FORMs and PROPs. For low level, you need to do this yourself. Any
- source code written with the original EA code may be used as an example.
- You will only need to deal with Context structures if you are using the
- low level routines. This structure is for reading and writing groups and their
- chunks. It's just a linked node type of structure for reading (nested) chunks.
- A Context structure must be created whenever another level is encountered.
- The parentContext field contains the address of another Context structure if
- this is not the top level. For example, if a LIST is encountered, a Context
- structure is created. For the first FORM in the LIST, another context structure
- is created. The parentContext field of the FORM's Context would contain the
- address of the LIST's Context structure. The parentContext field of the LIST's
- Context structure would be NULL (as long as it wasn't inside of still amother
- group). You must read or skip over all the chunks in the FORM before you try to
- examine what comes next in the LIST. You can read a chunk via GetChunkHdr() and
- IFFReadBytes. You can skip a chunk by calling GetChunkHdr() a second time for
- the next chunk's ID.
- The UserData field is set to the parent's Userdata field by certain
- routines. In this way, every Context's UserData is the same as its parentCon-
- text's UserData.
- If you use the low level routines properly, the lib will take care of
- padding out chunks to even bytes, and keeping track of the total bytes read
- in or written out.
- The bound field of the Context merits special mention in that it appears to
- have no useful purpose. If you place any value other than UNKNOWN there, it
- serves as the MAX number of bytes that can be written. So for example, if the
- bound = 10000, then you will be prevented from writing out a total of more than
- 10000 bytes. For most IFF applications, it isn't initially known how many bytes
- will eventually be written. All of the IFF writers which I've seen always set
- this to UNKNOWN. Also, it doesn't seem useful to have the code impose a
- barrier. AmigaDOS already returns an error if you attempt to write beyond a
- disc's capacity. I can only assume that this "feature" was included so that
- this code would work on other computers with stranger DOS than the Amiga. This
- field is used in PutCkEnd to determine whether the lib needs to adjust UNKNOWN
- chunkSizes to the real value after it writes out a chunk. If EA had used a bit
- flag for UNKNOWN rather than a value to be compared against the bound field,
- then the code could be smaller and faster. Then again, the original code was
- written in C, a language invented by and for people who can't be bothered with
- trivial details like setting bits.
-
- ========= Low Level Reader Routines ========
-
- For reading a chunk, the procedure is to allocate a Context structure,
- initialize it with OpenRIFF or OpenRGroup, read the chunks with GetChunkHdr
- (and its kin) and IFFReadBytes, and close the Context with CloseRGroup or
- EndRGroup.
-
- IFFP = OpenRIFF(fileHandle, Context)
- d0 d1 a0
- Given an open file, this initializes a Context spanning the whole file.
- ASSUMES context was allocated by caller but not initialized.
- ASSUMES caller doesn't deallocate the context before calling CloseRGroup.
- Returns NOT_IFF IFFP code if the file is too small for even a chunk header.
-
-
- FileSize = FileLength(fileHandle)
- d0 d1
- Returns the length of the whole file or else a negative IFFP error code of
- NO_FILE (fileHandle=0) or DOS_ERROR. Does not change your current position
- in the file.
-
-
- IFFP = OpenRGroup(parent, new) passed 2 Context structures
- d0 a0 a1
- Open the remainder of the current chunk as a group read context.
- This will be called just after the group's subtype ID has been read
- (automatically by GetChunkHdr for LIST, FORM, PROP, and CAT) so the
- remainder is a sequence of chunks.
- This sets new's UserData = parent's UserData.
- ASSUMES new context allocated by caller but not initialized.
- ASSUMES caller doesn't deallocate the context or access the parent context
- before calling CloseRGroup on the new context.
- BAD_IFF error if context end is odd or extends past parent.
-
-
- IFFP = CloseRGroup(context)
- d0 a0
- Close a group read context, updating its parent context.
- After calling this, the old context may be deallocated and the parent
- context can be accessed again. It's okay to call this particular procedure
- after an error has occurred reading the group.
-
-
- ID = GetChunkHdr(context)
- d0 a0
- Skip any remaining bytes of the previous chunk and any padding, then
- read the next chunk header into context's chunkID and chunkSize fields.
- If the chunkID is LIST, FORM, CAT, or PROP, this automatically reads the
- subtype ID into context's subID field.
- Caller should dispatch on groupID (and typeID) to an appropriate handler.
- RETURNS the chunkID (the 4 ascii bytes of the new chunk header) if it found
- another chunk. Otherwise, it will return one of the following errors:
- 1). END_MARK if there are no more chunks in this context
- 2). NOT_IFF if at the top level and it isn't a FORM, LIST, or CAT
- 3). BAD_IFF if a malformed chunk, the chunkSize is negative or too big for
- containing context, ID isn't positive, or we hit end-of-file.
- Note that if an error, bit #31 of d0 will be set and so you can either
- 1). move.l d0,d1
- bmi to an error routine.
- 2). btst.l #31,d0
- bne to an error routine.
- See also GetFChunkHdr and GetPChunkHdr, below.
-
-
- ID = GetFChunkHdr(context)
- d0 a0
- Used by a FORM handler. It simply calls GetChunkHdr and returns BAD_IFF if
- it encounters an imbedded PROP.
-
-
- ID = GetPChunkHdr(context)
- d0 a0
- Used by a PROP handler. It simply calls GetChunkHdr and returns BAD_IFF if
- it encounters an imbedded PROP, LIST, CAT, or FORM.
-
-
- IFFP = IFFReadBytes(nBytes, context, buffer)
- d0 d0 a0 a1
- Read the specified number of data bytes of current chunk. (Use OpenGroup,
- etc. instead to read the contents of a group chunk.) You can call this
- several times to read the data piecemeal.
- CLIENT_ERROR if nBytes < 0. SHORT_CHUNK if nBytes > remaining bytes
- which could be due to a application bug or a chunk that's shorter than it
- ought to be (bad form). (If CLIENT_ERROR or SHORT_CHUNK, IFFReadBytes won't
- read any bytes.)
-
-
- IFFP = SkipFwd(bytes, context)
- d0 d1 a0
- Skip over bytes in a chunk. Won't go backwards.
- Updates context's position but not context's bytesSoFar.
-
-
- NOTE: The original EA code's SkipGroup() routine has been eliminated
- since the library is set up to automatically parse LISTS and PROPS.
-
-
- IFFP = GetCMAP(Context, colorMap, pNColorRegs)
- d0 d0 a0 a1
- Reads in a CMAP chunk and creates the colorMap at passed address.
- pNColorRegs is passed in as a pointer to the number of ColorRegisters
- caller has space to hold. GetCMAP sets to the number actually read.
-
-
- IFFP = GetBODY( bitmap, mask, context, BMHD )
- d0 d0 d1 a0 a1
- Reads the BODY into the passed bitmap's planes, decompressing if necessary.
- Passed the addresses of a BitMap, mask plane (or NULL), the context
- structure, and the loaded BitMap header chunk.
-
-
- BOOL = UnPackRow(dstBytes0, srcBytes0, Source, Dest)
- d0 d1 d3 a2 a3
- Converts data from "cmpByteRun1" run compression.
- control bytes:
- [0..127] : followed by n+1 bytes of data.
- [-1..-127] : followed by byte to be repeated (-n)+1 times.
- -128 : NOOP.
- Unpacks one row, returning the source and destination addresses when it
- produces dstBytes bytes. This routine no longer uses POINTERS to POINTERS
- as in the original Elec Arts code. Also, args in a different order.
-
- =========== Low Level Writing (Save) Routines ==========
-
- These routines will random access back to set a chunk size value when the
- caller doesn't know it ahead of time (UNKNOWN size).
-
- The overall scheme is to open an output Group Context via OpenWIFF or
- OpenWGroup, call either PutCk or {PutCkHdr {IFFWriteBytes}* PutCkEnd} for
- each chunk, then use CloseWGroup to close the Group Context.
-
- To write a group (LIST, FORM, PROP, or CAT), call StartWGroup, write out
- its chunks, then call EndWGroup. StartWGroup automatically writes the
- group header and opens a nested context for writing the contents.
- EndWGroup closes the nested context and completes the group chunk.
-
- IFFP = OpenWIFF(limit, fileHandle, new) new is a pointer to a GroupContext
- d0 d0 d1 a0 structure
- Given a file open for output, initialize a new, write context.
- The "limit" arg imposes a fence or upper limit on the logical file
- position for writing data in this context. Pass in UNKNOWN to be limited
- only by disk capacity.
- ASSUMES new context structure allocated by caller but not initialized.
- ASSUMES caller doesn't deallocate the context before calling CloseWGroup.
- The caller is only allowed to write out one FORM, LIST, or CAT in this top
- level context (see StartWGroup and PutCkHdr).
- CLIENT_ERROR if limit is odd.
-
-
- IFFP = StartWGroup(groupType, groupSize, subtype, parent, new)
- d0 d0 d1 d2 a0 a1
- parent and new are the addresses of Context structures, groupType and
- subtype are IDs, groupsize is a LONG
- Start writing a group (presumably LIST, FORM, PROP, or CAT), opening a
- nested context. The groupSize includes all nested chunks + the subtype ID.
- The subtype of a LIST or CAT is a hint at the contents' FORM type(s). Pass
- in FILLER (" ") if it's a mixture of different kinds.
- This writes the chunk header via PutCkHdr, writes the subtype ID via
- IFFWriteBytes, and calls OpenWGroup. The caller may then write the nested
- chunks and finish by calling EndWGroup.
- The OpenWGroup call sets new's ILBMFrame to parent's ILBMFrame.
- ASSUME new context structure allocated by caller but not initialized.
- ASSUME caller doesn't deallocate the context or access the parent context
- before calling CloseWGroup.
- ERROR conditions: See PutCkHdr, IFFWriteBytes, OpenWGroup.
-
-
- IFFP = OpenWGroup(parent, new) parent and new are GroupContext structures
- d0 a0 a1
- Open the remainder of the current chunk as a group write context.
- This is normally only called by StartWGroup.
- Any fixed limit to this group chunk or a containing context will impose
- a limit on the new context.
- This will be called just after the group's subtype ID has been written
- so the remaining contents will be a sequence of chunks.
- This sets new's UserData = parent's UserData.
- ASSUME new context structure allocated by caller but not initialized.
- ASSUME caller doesn't deallocate the context or access the parent context
- before calling CloseWGroup.
- CLIENT_ERROR if context end is odd or PutCkHdr wasn't called first.
-
-
- IFFP = EndWGroup(old)
- d0 a0
- End a group started by StartWGroup.
- This just calls CloseWGroup and PutCkEnd.
- ERROR conditions: See CloseWGroup and PutCkEnd.
-
-
- IFFP = CloseWGroup(old) old is the address of a Context structure
- d0 a0
- Close a write context and update its parent context.
- This is normally only called by EndWGroup.
- If this is a top level context (created by OpenWIFF) we'll set the file's
- EOF (end of file) but won't close the file.
- After calling this, the old context may be deallocated and its parent
- context can be accessed again.
- Amiga DOS Note: There's no call to set the EOF. We just position to the
- desired end and return. Caller must Close file at that position.
- CLIENT_ERROR if PutCkEnd wasn't called first.
-
-
- IFFP = PutCk(chunkID, chunkSize, context, data)
- d0 d0 d1 a0 a1
- Writes a whole chunk to a Context. This writes the chunk ID, chunkSize,
- data bytes, and (if needed) a pad byte. It also updates the Context to
- reflect how many bytes have been written to the file. Returns CLIENT_ERROR
- if chunkSize = UNKNOWN. This is because you must know how many bytes of
- data you wish to write in order to use this routine. (i.e. Use this routine
- instead of a PutCkHdr/IFFWriteBytes/PutCkEnd series of calls when you know
- exactly how many bytes will be in the chunk). See also PutCkHdr errors.
-
-
- IFFP = PutCkHdr(chunkID, chunkSize, context)
- d0 d0 d1 a0
- Writes just an 8 byte chunk header. The chunk header consists of the 4 byte
- ascii ID, and the chunkSize LONG. You should follow this will any number of
- calls to IFFWriteBytes in order to write out the chunk data. Finally, when
- all the chunk data is output, call PutCkEnd.
- If you don't yet know how big the chunk is, pass in chunkSize = UNKNOWN,
- then PutCkEnd will set the chunkSize for you later. This method is used
- when you really don't know how many data bytes will eventually get written
- out. (i.e. maybe you're compressing the data as it's being written out and
- you don't want to bother with knowing or keeping track of how many bytes
- have been written out. The lib does this for you as long as you specify
- chunkSize = UNKNOWN and you use only the IFFWriteBytes routine to write data
- to the file).
- Otherwise, IFFWriteBytes and PutCkEnd will ensure that the specified
- number of bytes get written.
- CLIENT_ERROR if the chunk would overflow the Context's bound, if
- PutCkHdr was previously called without a matching PutCkEnd, if chunkSize
- < 0 (except UNKNOWN), if you're trying to write something other
- than one FORM, LIST, or CAT in a top level (file level) context, or
- if chunkID <= 0 (these illegal ID values are used for error codes).
-
-
- IFFP = IFFWriteBytes(nBytes, context, data)
- d0 d0 a0 a1
- Write nBytes number of data bytes for the current chunk and update the
- Context.
- Returns CLIENT_ERROR if any of the following conditions:
- 1). Writing nBytes of data would overflow the Context's limit or
- current chunk's chunkSize. If you have specified these fields to be
- UNKNOWN, then this condition is not applicable.
- 2). If PutCkHdr wasn't called first.
- 3). nBytes < 0 (i.e. ridiculously large).
-
-
- IFFP = PutCkEnd(context)
- d0 a0
- Complete the current chunk, write a pad byte if needed, and update the
- Context.
- If current chunk's chunkSize = UNKNOWN, this goes back and sets the
- chunkSize in the file.
- CLIENT_ERROR if PutCkHdr wasn't called first, or if the application hasn't
- written 'chunkSize' number of bytes with IFFWriteBytes.
-
-
- IFFP = InitBMHdr(masking, compression, transparentColor,
- d0 d0 d1 d2
- pageWidth, pageHeight, bmHdr0, bitmap)
- d3 d4 a0 a1
- Initializes a BitMap Header (BMHD) chunk to the passed values, and sets the
- BMHD's aspect ratio.
-
-
- IFFP PutCMAP(depth, context, colorMap)
- d0 d0 a0 a1
- Writes out the passed colorMap (actually colorTable) as a CMAP chunk.
-
-
- IFFP = PutBODY(mask, context, bmHdr, bitmap)
- d0 d0 d1 a0 a1
- Writes the BODY chunk to disk in compressed or uncompressed form.
-
-
- bytes, newSource, newDest = PackRow(rowSize, pSource, pDest)
- d0 a0 a1 d0 a0 a1
- Given addresses of source and dest, packs one row, returning the new source
- and destination addresses in a0 and a1. RETURNs count of packed bytes in d0.
- Please note that this routine needs the actual addresses to the source and
- destination buffers unlike the original EA code which wanted PTRS to PTRS.
- That technique is unnecessary for assembly applications which can access
- multiple return values in several registers. I decided to put the ineffic-
- iency where it really belongs; in the C application. For C programmers, the
- new source and dest addresses can be found at sourceptr and destptr respec-
- tively. These are globals contained in the module ILBMInterface.asm which
- must be assembled and linked with your application. These globals can be
- accessed after a call to PackRow so that you'll have the addresses to pass
- on the next, subsequent call. Of course, you'll be able to access the
- returned number of packed bytes as a normal return. Ultimately what this
- means is that if you call PackRow from a C application, you can never make
- that application fully re-entrant (unless you put a FORBID/PERMIT around
- the call).
-
-
- **************************************************************************
- 9). Adapting old IFF applications
-
- If you already have an old application that uses the Electronic Arts code,
- there are several approaches that you could take to adapt the program to use
- the ilbm lib instead.
- The easiest approach is to ask yourself, "Can I use LoadIFFToWindow() or
- SaveWindowToIFF()?" If all that you're doing with IFF is saving or loading
- ILBM pictures in standard Intuition windows, the answer is yes. Get rid of ALL
- the EA code and INCLUDE files. Dump ReadPict.c, ReadIFF.c, IFFr.c, etc. Follow
- the example ShowPic.c (or ShowPic.asm). Then check the size of your program.
- If you're doing ANIMs, non-ILBM (SMUS, 8SVX, etc.) or overscan screen views,
- you'll need to persue a second approach. Use all of the low level routines of
- the lib in place of the similiarly named EA functions. Section 5 of this
- document lists all of the low level functions (i.e. GetCMAP, OpenWGroup, PutCk,
- GetBODY, etc.) Remember that these are functionally identical to the original
- EA code.
- For example, you've probably got OpenRGroup() somewhere in your program.
- (Check the EA code that you're using as well). Found OpenRGroup()? Did you
- modify it? No? Then you're all set. Rip the function OpenRGroup() out of your
- code. Add the line
-
- #include "ilbm_lib.h"
-
- at the top of the source code. You are now using the ilbm lib's OpenRGroup()
- instead of the original EA code.
- Do this with all of the low level routines you find throughout your program.
- Just be careful to note the order of the parameters. They may have changed,
- and if so, you will have to modify any routine that calls the replaced func-
- tion. For example, a call to the EA routine, PutCkHdr() is as follows:
-
- PutCkHdr(context, ckID, ckSize);
-
- The ilbm lib's PutCkHdr has a different order for those 3 args:
-
- PutCkHdr(ckID, ckSize, context);
-
- Otherwise, the two functions are the same. Several low level routines have a
- different arg order. This was done for the sake of efficiency in the lib's
- C interface. Also, note that a few low level functions no longer need
- certain args. For example, the EA GetBODY() is:
-
- GetBODY( context, bitmap, mask, bmHdr, buffer, bufsize );
-
- The ilbm lib is:
-
- GetBODY( bitmap, mask, context, bmHdr );
-
- The library supplies its own buffer. You don't have to. (That's one more thing
- you can get rid of in your source code.)
- Finally, you should get rid of any EA INCLUDE files, and all INCLUDE
- statements in your program that reference those files. You don't need them.
- "ilbm_lib.h" replaces all EA include files (i.e. IFF.h, PutPict.h, ReadPict.h,
- Packer.h, and ILBM.h). When you link your program, you will have to assemble
- the file "ILBMInterface.asm", and link with that.
- Of course, if you can make use of the mid level functions by rewriting your
- program to use all of their special features, your program will be that much
- smaller and faster. Unfortunately, this is not as straightforward as substitu-
- ting the low level functions.
-
-
- *****************************************************************************
- 10). ANIM Support
-
- There are a few routines to assist in the construction of an ANIM player.
- These routines can unpack BODY and DLTA chunks, modifying a BitMap's planes
- in order to construct the next frame of an animation. Also there is a routine
- to setup a BitMap structure initially based on a loaded BMHD chunk.
- The normal procedure for an anim player would be to either replace the lib's
- default 'FORM' handler with a custom handler, or use a CHUNKhandler set up to
- handle ID_BODY, ID_ANHD, and ID_DLTA. This handler should load the first
- frame's BMHD and BODY, and the ANHD and DLTA for all subsequent frames. Also,
- you should set up a PROPhandler to parse any ID_ANHD in an ILBM PROP.
- Then you can play back the anim as follows:
-
- 1). Allocate a raster large enough to hold the decompressed image. You can
- determine the size by the fields in the BMHD.
-
- height = BMHD's h or pageHeight, whichever is larger
- planedepth = numPlanes * height
- width = BMHD's w or pageWidth, whichever is larger
- RowBytes = (15+width)/8, rounded down to the nearest integer
- RasterSize = RowBytes * planeDepth
- get CHIP mem for the raster
-
- 2). Setup the BitMap structure to be used for displaying the ANIM by calling
- the ilbm lib's SetupBitMap. Pass your allocated raster to SetupBitMap.
- You will need to set up the View, ViewPort, and colorMap yourself.
-
- 3). Decompress the BODY into the BitMap's planes (i.e. your allocated
- raster) using the library's DecompBODY(). For double-buffered animation,
- you will need to setup an identical Bitmap/raster and copy the first
- image into the new raster.
-
- 4). Display the BitMap's planes (i.e. the first frame).
-
- 5). Modify the BitMap's planes for the next frame via DecompDLTA().
-
- 6). Repeat from step 4 until no more frames.
-
-
- Here are the 3 routines that you'll use in an ANIM player.
-
- ===================== SetupBitmap() ===================
- SetupBitmap(raster,bitmap,BMHD)
- d0 a0 a1
-
- Initializes passed bitmap's depth, rows, and BytesPerRow based on passed
- BMHD's (BitMapHeader) x, y, and numPlanes, then stores addresses into bitmap's
- planes[] of each plane within the passed raster. (All the planes in the raster
- form 1 contiguous CHIP mem block). Raster must be large enough for numPlanes
- * BMHD's (x+15)/8 * BMHD's y.
- After an ANIM is loaded, and you allocate a raster (via AllocRaster maybe)
- based on the BMHD's x, y, and numPlanes, you can use this routine to setup
- a BitMap structure in order to decompress the BODY with DecompBODY and
- start calling DecompDLTA.
-
-
- ================== DecompBODY() ===================
- DecompBODY(BODYdata, BMHD, Bitmap)
- a0 a1 a2
- Decompresses a BODY chunk's data into the BitMap's planes based on the
- passed BMHD (BitMapHeader) chunk's w,y,numPlanes. This can be used to make
- the first frame of an ANIM.
-
-
- =================== DecompDLTA() ==================
- DecompDLTA(DLTAdata,Bitmap)
- a0 a2
- Decompresses a DLTA chunk's data into the BitMap's planes (which must still
- have the previous frame's image). This routine calls MakeYTable and
- DecodeVKPlane, and can be used to "make" the next frame of an animation
- being double-buffered.
-
-
- You probably won't need to use these next 2 functions unless you're doing
- something unusual with DLTA data.
-
- ===================== MakeYTable() =====================
- MakeYTable(width, height, table)
- d0 d1 a0
- Makes a ytable for use with DecodeVKPlane. Table should be an memblock
- capable of holding 500 WORDs (1000 bytes).
-
-
- =================== DecodeVKPlane() ========================
- DecodeVKPlane(linebytes,ytable,in,out)
- d0 d1 a0 a1
-
- By Jim Kent. Modified JG. Copyright 1987 Dancing Flame all rights reserved.
- Decompresses a DLTA chunk in vertical-byte-run-with-skips compression mode.
-
- where in is a bit-plane's worth of vertical-byte-run-with-skips data
- and out is a bit-plane that STILL has the image from last frame on it.
- Linebytes is the number of bytes-per-line (USHORT) in the out bitplane, and
- it should certainly be noted that the passed variable ytable must be
- initialized to point to a multiplication table of 0*linebytes, 1*linebytes
- ... n*linebytes before this routine is called.
- Each entry in ytable is a USHORT.
-
- The format of "in":
- Each column of the bitplane is compressed separately. A 320x200
- bitplane would have 40 columns of 200 bytes each. The linebytes
- parameter is used to count through the columns, it is not in the
- "in" data, which is simply a concatenation of columns.
-
- Each columns is an op-count followed by a number of ops.
- If the op-count is zero, that's ok, it just means there's no change
- in this column from the last frame.
- The ops are of three classes, and followed by a varying amount of
- data depending on which class.
- 1. Skip ops - this is a byte with the hi bit clear that says how many
- rows to move the "dest" pointer forward, ie to skip. It is non-
- zero
- 2. Uniq ops - this is a byte with the hi bit set. The hi bit is
- masked down and the remainder is a count of the number of bytes
- of data to copy literally. It's of course followed by the
- data to copy.
- 3. Same ops - this is a 0 byte followed by a count byte, followed
- by a byte value to repeat count times.
-
-
- ***************************************************************************
- 11). Image Size/Scaling
-
- When you ask the lib to load an image into an already opened window, some-
- times the image won't be the exact same size as the window. You may be trying
- to load a HIRES picture (640 x 200) into a LORES screen (320 x 200), or vice
- versa. You may even be trying to load a picture larger than a standard Amiga
- screen size (i.e. 660 x 220) into a standard size screen. The library has a
- routine that can automatically "scale" the picture to fit your window. LORES
- pictures will be "expanded" to fill a HIRES display, and HIRES will be "shrunk"
- to fit a LORES. Non-standard size pictures will likewise be scaled to fit a
- standard screen. Because of this, you never need worry about what size picture
- a user has chosen to display in your window. It will always fit.
- The lib's scaling routine makes multiple calls to graphics lib routines
- which appear to do temporary mem alloc/dealloc. Unfortunately, the dealloca-
- tion is not done in the same order as the allocation. The net result: severe
- memory fragmentation. Oddly enough, this problem appears to only inflict LORES
- pictures being scaled into a HIRES screen. A HIRES picture into a LORES screen
- works fine. It appears likely that you may not be able to load a LORES picture
- into a HIRES screen twice in a row. Alas, the Amiga operating system is not
- smart enough to re-coalese free blocks when it could. You have to reboot the
- computer. You want smart memory coalesing, buy a MAC. Despite this, I decided
- that scaling the picture yielded more pleasing results than cropping it. If
- memory fragmentation problems persist, or you don't want a "smaller" picture to
- be scaled to fill a "larger" display area, set the NOSCALE flag of the ILBM-
- Frame's iUserFlags. This will cause LORES pictures to only fill as much of a
- larger display as they normally would. Larger pictures are always scaled to
- fit smaller displays.
- This scaling is meant to fit an image with a given width and height into
- a screen with a different width and/or height. The lib does not currently
- adjust for different depths (number of planes). For this reason, an image
- with a depth of 5 will probably end up with "weird" colors when loaded into
- a screen with a depth of 4. Perhaps in the future, a routine will be added
- to interpolate color tables if there appears to be interest in such a feature.
- Note that the process of scaling a picture to fit a different size screen
- is notably slow. Get used to it.
-
- Here is the routine that scales a section of one BitMap to fit into a
- section of another RastPort. It is passed a standard, graphics structure
- called a Rectangle.
-
- BOOL ScaleImage(dest_rectangle,source_rectangle,dest_rport,source_bitmap)
- d0 a0 a1 a2 a4
-
- Passed pointers to the source bitmap, source rectangle structure (current
- dimensions), destination rastport (to fit source into), and dest rectangle
- struct (desired dimensions). Scales the rectangular chunk (as described by
- source_rectangle) of source bitmap into the rectangular chunk (as described by
- dest_rectangle) of destination rastport. It achieves this by remapping the
- color value of each pixel, discarding or adding extra pixels per the scaling
- dimensions using an array for the colors. This does not create a larger or
- smaller palette of colors if the # of planes is different. It simply fits one
- image of given width and height into a different size raster. Alters the passed
- sorce BitMap's planes while transfering. It only does one line at a time of the
- source so it is SLOW.
-
- Returns a 1 if success, 0 if error (no mem for color array).
-
- If you set the ILBMFrame's Flags ADJUSTVIEW bit and allow the library to
- open the window/screen combo, the screen's view will be adjusted so that the
- image is centered in the amiga's display. This means that an overscanned image
- (i.e. larger than 320 x 200 if LORES, 640 x 200 if HIRES) will be centered in
- the display. This also takes INTERLACE and PAL into account. On the other hand,
- an image smaller than a standard Intuition screen will be centered as well.
-
-
- ***************************************************************************
- 12). ILBMlib Error Msgs
-
- Your calling routines should check all returned IFFP error codes. Don't
- press on after an error! The lib routines try to have no side effects if an
- error, except that partial I/O is sometimes unavoidable. Any routine could
- return DOS_ERROR. In that case, ask DOS for the specific error code, if
- desired, via the DOS library's IOErr() routine.
-
- Here is a routine to help display error messages to the user. This routine
- returns a pointer to a NULL-terminated string which describes the IFFP error
- number. You can then display this string to the user. See the INCLUDE files
- for the meaning of certain IFFP codes.
-
- String = GetIFFPMsg(IFFP)
- d0 d0
-
-
- ****************************************************************************
- 13). Misc Routines
-
- Here's a routine to make the mouse pointer in the passed window "disappear".
- Use Intuition's ClearPointer() or SetPointer() to change it again later.
-
- BlankPointer(window)
- a0
-
-
- ****************************************************************************
- 14). Additional Comments
-
- An effort was made to test all of the lib functions in a variety of ways,
- but I'm sure that there are things which have escaped me. Please send any
- bug reports to the above address. I am not offering free consultation to
- anyone, but if you have a particular problem or question, I will try to
- assist. If a particular piece of code appears not to work with the library,
- I would be interested in seeing the code. There will almost certainly be new,
- improved lib versions in the future. These will be sent to Fred Fish.
- An article in AmigaWorld's premiere Tech Journal has an assembly example of
- using this library in an ANIM player, in which I demo using some ANIM oriented
- routines of the library.
- The modules that you should have are as follows:
-
- ILBMLib.Doc this text file
- ilbm.library the library (to be copied to your boot disk's libs drawer)
- IFF.i assembly language include file
- ILBM_Lib.h C Include file
- ShowPic.c a C example of using the lib's high level functions
- ILBMInterface.asm the awful "poot" that is required for any C example
- BasicILBM an AmigaBasic example
- ilbm.bmap the bmap file for the Basic example
- ilbm_lib.fd the Basic fd file
- BasicUsers additional info for Basic programmers
- ShowPic.asm an assembly example using the high level functions
- ANIMInfo.asm an assembly example of installing a custom FORM handler
- and using mid/low level functions
- ANIMInfo an executable of the above
- ShowPic a re-entrant ILBM viewer with color-cycling (operates
- like CBM's "Display" program)
- IFFinfo.c a C example of installing a custom FORM handler and using
- mid/low level functions
- AboutIFF a straightforward explanation of IFF file format
- Play8SVX.c a C example for using mid/low level routines to load an
- 8SVX file
-
-
- *****************************************************************************
- 15). Acknowledgements
-
- The ilbm.library is based upon much code placed in the public domain by
- several individuals. The programmers who offer such favors are the ones
- who are really responsible for the Amiga still being a viable product.
- Once again, I want to mention the people who helped make this public
- domain offering possible:
-
- Jerry Morrison, Steve Shaw, and Steve Hayes, Electronic Arts.
- C. Scheppner, CATS.
- Jim Kent, Dancing Flame.
-
- This is one of those few efforts in which I didn't steal "something" from
- Bryce Nesbitt. Bryce is such a good programmer and documentator that it is
- a fluke that he actually works for CBM.
-
-
- *****************************************************************************
- 16). Scary Legal Stuff
-
- "OK. IF I USE IT, WHAT'S IT GONNA COST ME."
-
- The ilbm.library may be used by and FREELY distributed with any application
- be it commercial or public domain.
- There are no pagan users fees, Trump-esque licenses, or other forms of rabid
- capitalist trickery associated with using this library and its support files.
- You do not even have to acknowledge the secret of your expedient and memory
- efficient IFF I/O routines.
- The only limitation is that you may not alter the actual executable of the
- ilbm.library, nor sell the library and its support files as a distinct product
- (i.e. represent it as such).
-
-
- ============================= FOOTNOTES ===============================
-
- ╣ The routines that read and write data to disc (IFFReadBytes and IFFWrite-
- bytes) use unbuffered I/O. An application that uses the buffered version
- of EA's code will probably be faster but certainly not as memory effic-
- ient. Wouldn't you rather wait an extra 15 seconds to load an ANIM file
- rather than not be able to view it at all because the loader is sucking
- up RAM? Besides, if you want to waste memory with buffers that remain idle
- except during disk I/O, why not use the CLI ADDBUFFERS command. This way,
- you can speed up the lib to comparable levels with the buffered IFF code,
- and also allow other disc I/O code to benefit. I realize that the concept
- of conserving RAM is not popular amoung non-assembly programmers, most
- of whom write applications which unnecessarily require 1 MEG to run well.
-
- ▓ In order to handle PROPs more efficiently and with less stack use than
- the original EA code, I made the following limitation.
- There should be no LIST imbedded within a LIST.
- In a file that violates this rule, PROP data from the outer LIST may
- affect chunks inside the inner LIST. Since I have never seen imbedded
- LISTs, I decided that such abominations don't deserve special treatment.
-